
import { ExecutiveContext, State,Executive, Autherizable, ActivityStatus, ActionType } from "../defination"
import { ActivityRecord, ByteArray, ExecutionRecord, ExecutiveRecord } from "../entity"
import {  ACTIVITY, ACTIVITY_STATUS, DEALER, EXECUTION_RECORD, NEXT, REQUEST, STATE_RECORD } from "../shared-symbols"
import { BaseAction } from "./action"
import type { BeginAction } from "./beginAction"
import { DealAction } from "./dealAction"
import { invokeExecutiveAction } from "../executive"
import { EndAction } from "./endAction"


export const ENTERED = Symbol('entered')

export class EnterAction
<
  TActivity extends Executive<TActivity,TState,TRecord,TContext>,
  TState extends State=State,
  TRecord extends ExecutiveRecord=ExecutiveRecord,
  TContext extends ExecutiveContext=ExecutiveContext
> extends BaseAction<TActivity,TState,TRecord,TContext>
{
  [ENTERED]?: boolean
  constructor(act:BeginAction<TActivity,TState,TRecord,TContext>){
    super(act)
  }
  public get entered():boolean{
    return this[ENTERED]===true
  }
  async invoke(context:TContext):Promise<DealAction<TActivity,TState,TRecord,TContext>|EndAction<TActivity,TState,TRecord,TContext>|null|undefined>{
    if (this[ENTERED]===true || this[ENTERED]===false)
      throw new Error('enter action can only be invoked once')

    const activity = this[ACTIVITY]
    const currentStatus = activity[ACTIVITY_STATUS]
    if (currentStatus !== ActivityStatus.INITIALIZED) {
      throw new Error('only can invoke enter action when the activity is initialized')
    }

    const result = await invokeExecutiveAction<TActivity,TState,TRecord,TContext>(this.activity,ActionType.ENTER,context)
    this[ENTERED] = result===false ? false : true
    if (result===false)
      return this.next
    

    const at = context.calendar.now()
    const dealerId = context[DEALER].id
    const nextStatus = ActivityStatus.ENTERED
    const statusBytes = context.enumConverter.toByteArray<ByteArray>(nextStatus,ActivityStatus,true)!

    const activityRecord = this[STATE_RECORD] as unknown as ActivityRecord
    activity[ACTIVITY_STATUS] = nextStatus
    activityRecord.activityStatus = statusBytes
    activityRecord.enterTime = at
    activityRecord.modifierId = dealerId
    activityRecord.updateTime = at

    const executionRecord = this[EXECUTION_RECORD] as ExecutionRecord
    executionRecord.activityStatus = statusBytes
    executionRecord.updateTime = at

    const fromStateName = context.enumConverter.toByteArray<ByteArray>(ActivityStatus.INITIALIZED,ActivityStatus,true)!
    await context.repository.enterActivity(
      activityRecord,
      executionRecord!,
      fromStateName,
      context[REQUEST].requestId
    )
    return this.next
  }

  get next():DealAction<TActivity,TState,TRecord,TContext>|EndAction<TActivity,TState,TRecord,TContext>|null|undefined{
    if (this[ENTERED]===undefined)
      return undefined
    
    if (this[NEXT]===undefined)  {
      if (this[ENTERED]===false)
        return this[NEXT] = new EndAction(this)
      else
        return this[NEXT] = new DealAction(this)
    }
    return this[NEXT] as DealAction<TActivity,TState,TRecord,TContext>
  }
}